/*
 * The MIT License (MIT)
 *
 * File: board.h
 *
 * Description: Contains the board specific defines.
 *
 * Copyright (c) 2013 Ed Crampton
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#if !defined(MCU) || (MCU == msp430g2452)
#include <msp430g2452.h>
#define TIMVECTOR TIMER0_A1_VECTOR
#else
#include <msp430g2231.h>
#define TIMVECTOR TIMERA1_VECTOR
#endif
#include <stdint.h>

//LogBase20 of led intensisty based on a percentage
const uint16_t intensity[] = {
								624,
								624,
								624,
								624,
								498,
								423,
								370,
								329,
								296,
								267,
								243,
								221,
								202,
								184,
								168,
								154,
								140,
								127,
								116,
								104,
								94,
								84,
								75,
								66,
								57,
								49,
								41,
								34,
								26,
								19,
								13,
								6,
								0};

#define ARRAY_SIZE 33

//Port 1 leds
#define LED_YELLOW 	BIT0
#define LED_CLEAR 	BIT1
#define LED_RED 	BIT2
//Port 1 light sensor in
#define SENSE_IN	BIT3

#define LED_PORT	P1OUT
#define LED_DDR		P1DIR
#define SENS_DDR	P1DIR
#define SENS_PORT	P1OUT

#define LED_ON(led) LED_PORT |= led
#define LED_OFF(led) LED_PORT &= ~led
#define LED_EN(led) LED_DDR |= led
#define SENSOR_EN(input) SENS_DDR &= ~input

typedef enum {
	UPDATE_CYCLE_SLOW = 20,
	UPDATE_CYCLE_MEDIUM = 10,
	UPDATE_CYCLE_FAST = 1,
	UPDATE_ALL = 200,
}mode;

static inline void incMode(uint8_t *mode)
{
	switch(*mode) {
	case UPDATE_CYCLE_SLOW:
		*mode = UPDATE_CYCLE_MEDIUM;
		break;
	case UPDATE_CYCLE_MEDIUM:
		*mode = UPDATE_CYCLE_FAST;
		break;
	case UPDATE_CYCLE_FAST:
		*mode = UPDATE_ALL;
		break;
	case UPDATE_ALL:
	default:
		*mode = UPDATE_CYCLE_SLOW;
	}
}

static inline void IncLed(uint16_t *led)
{
	*led = *led << 1;
	if(*led > LED_RED) { *led = LED_YELLOW; }
}
static void AdcInitialize();

int pos = 0;   // Index to PWM's duty cycle table (= brightness)
unsigned char led = 1; //start with 1.0
unsigned char mEnableLed = 0;
unsigned int light_timer = 0;
#define ADC_REF 2.5;

int main(void)
{
  // Stop watchdog
  WDTCTL = WDTPW + WDTHOLD;

  // Set clock to 1 MHz
  DCOCTL= 0;
  BCSCTL1= CALBC1_1MHZ;
  DCOCTL= CALDCO_1MHZ;

  // SMCLK = 1 MHz / 8 = 125 KHz (SLAU144E p.5-15)
  BCSCTL2 |= DIVS_3;

  LED_EN(LED_RED | LED_YELLOW | LED_CLEAR);
  SENSOR_EN(SENSE_IN);
   
   AdcInitialize();
   
   LED_OFF(LED_RED);
   LED_OFF(LED_YELLOW);
   LED_OFF(LED_CLEAR);

  TACCR0 = 625;
  // Source Timer A from SMCLK (TASSEL_2), up mode (MC_1).
  // Up mode counts up to TACCR0. SLAU144E p.12-20
  TACTL = TASSEL_2 | MC_1 | TAIE;

  TACCTL2 = OUTMOD_7 | CCIE;

  // Initial CCR1 (= brightness)
  TACCR2 = 0;

  // LPM0 (shut down the CPU) with interrupts enabled
  __bis_SR_register(CPUOFF | GIE);

  // Silly return to make gcc happy
  return 0;
}

#pragma vector=TIMVECTOR
__interrupt void
tim_isr (void)
{
	static uint16_t current_led = LED_YELLOW;
	static uint8_t mode = UPDATE_CYCLE_SLOW;
	static uint16_t count = 0;
   switch( TA0IV )
   {
      case TA0IV_TACCR2 : //interrupt for the duty cycle
      {
    	  static int current_index = 0;
    	  static int update=0;
    	  static int modifier=1;
    	  //Based on ambient sensor turn on or off the led
         if(mEnableLed == 0)
            return;

         //ensure all leds are off
         LED_OFF(current_led);
         // Clear interrupt flag
         TACCTL2 &= ~CCIFG;

         if( update > mode) {
        	 TACCR2 = intensity[current_index];

        	 current_index+=modifier;
			 if(current_index >= ARRAY_SIZE) {
				 current_index = 32;
				 modifier = -1;
				 IncLed(&current_led);
			 }
			 else if(current_index < 0)
			 {
				current_index = 0;
				modifier = 1;
			 }
        	 update = 0;
         }
         ++update;

         if(mode == UPDATE_ALL)
         {
        	 update = 0;
        	 TACCR2 = 0;
         }
         // Wait to set the new TACCR1 until TAR has gone past it, so that we
         // don't get interrupted again in this period.


         //TACCR2 = new_ccr1 * 3;
         break;
      }
      case TA0IV_TAIFG: //overlfow of the flag
      {
         ++light_timer;
         ++count;
         if(count == 5000)
         {
        	 count = 0;
        	 incMode(&mode);
         }
         if(light_timer > 1000)
         {
            light_timer = 0;
            ADC10CTL0 |= ENC + ADC10SC; //start conversion of ADC
         }
         //testing
         if(mEnableLed == 0)
            P1OUT= 0x00;
         else if( mode == UPDATE_ALL) {
			 LED_OFF(current_led);
			 IncLed(&current_led);
			 LED_ON(current_led);
         }
		 else if(TAR < TACCR2)
			LED_ON(current_led);
         else
        	 LED_OFF(current_led);
         break;
      }
   }
}

static void AdcInitialize()
{
   ADC10CTL1 = INCH_6 + ADC10DIV_7;            // Repeat single channel 5
   ADC10CTL0 = SREF_1 + ADC10SHT_3 + ADC10ON + + REF2_5V + REFON + ADC10IE; //Turn on reference out sample auto ADC10 on sref = 1.5 ref and Vss
                                                      //temperature is in 10mV per degree should never read about 1.5V (150 F)
   ADC10AE0 |= 0x40;                         // P1.6 ADC option select

   return;
}

//light sensor adc used to enable the led
#pragma vector=ADC10_VECTOR
__interrupt void
adc10_isr (void)
{
   float voltage;
   voltage = ((float)ADC10MEM/1023.0) * ADC_REF;

  if(voltage < (float) 2.2)
   mEnableLed = 1;
  else
   mEnableLed = 0;
}

